package chatController;

import java.io.File;

import model.SharingFiles;
import model.SharingMessages;
import model.State;
import model.StateChangeException;
import model.SystemState;
import model.User;
import model.UserException;
import model.Users;

/**
 * 
 * @author Pierre & Michael
 *
 */
/**
 * Controller of the chat system
 */
public class ChatController {
	/**
	 * Called before a class read or write the connected users list
	 */
	private static Users users;
	/**
	 * Model observable used to manage the sharing of messages
	 */
	private static SharingMessages sharingMessages;
	/**
	 * Model observable used to manage the sharing of files
	 */
	private static SharingFiles sharingFiles;	
	/**
	 * Model used to manage the state of the system
	 */
	private static SystemState state;
	
	/**
	 * 
	 * @param users
	 * @param sharingMessages
	 * @param sharingFiles
	 * @param state
	 */
	public ChatController(Users users, SharingMessages sharingMessages, SharingFiles sharingFiles, SystemState state){
		ChatController.users = users;
		ChatController.sharingMessages= sharingMessages;
		ChatController.sharingFiles = sharingFiles;
		ChatController.state = state;
	}
	
	/**
	 * Method called to initialized a lot of things
	 */
	public void performOpenChat() {
		try {
			state.changeState(State.disconnected);
		} catch (StateChangeException e) {
			System.out.println("FATAL ERROR : Reboot system now.");
		}
	}
	
	/**
	 * Called to connect the local user after he provides a username
	 * @param username username of the local user
	 * @throws UsernameEmptyException
	 * @throws CharacterNotAcceptedException
	 */
	public void performConnect(String username) throws UsernameEmptyException, CharacterNotAcceptedException {
			String name = this.checkUsername(username);
			users.setLocalUser(name);			
	}
	
	/**
	 * Called when the hello message has been broadcasted
	 */
	public void helloSent() {
		try {
			state.changeState(State.connected);
		} catch (StateChangeException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Called when a hello message has been received from a remote user
	 * @param username username of the remote user
	 * @param hostName hostname of the remote device
	 */
	public void helloRcvd(String username, String hostName){
		try {
			users.checkIn();
			users.addUser(username, hostName); 
			users.checkOut();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Called when a chat message has to be sent
	 * @param msg message to send
	 * @param dest recipient of the message
	 */
	public void performSend(String msg, User dest){
		sharingMessages.newMessage(true, msg, dest);
	}
	
	/**
	 * called when a chat message has been received
	 * @param msg message received
	 * @param username username or the transmitter
	 * @param host hostname of the transmitter's device
	 */
	public void messageRcvd(String msg, String username, String host){
		try {
			sharingMessages.newMessage(false, msg, users.getUser(username, host));
		} catch (UserException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Called when a remote user wants to send a file to the local user
	 * @param fileName name of the file
	 * @param username usersname of the remote user
	 * @param host hostname of the remote user's device
	 */
	public void fileNotifRcvd(String fileName, String username, String host) {
		try {
			sharingFiles.newRcvdNotif(fileName, users.getUser(username, host));
		} catch (UserException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * called when the local user wants to send a file
	 * @param file file to send
	 * @param dest address of the recipient
	 * @throws StateChangeException
	 */
	public void sendFile(File file, User dest) throws StateChangeException {
		state.changeState(State.busy);
		sharingFiles.newSendNotif(file, dest);
	}
	
	/**
	 * called when a file has been sent
	 */
	public void fileSent() {
		sharingFiles.fileSent();
		try {
			state.changeState(State.connected);
		} catch (StateChangeException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * called when the remote user accepted or refused to receive a file
	 * @param isAccepted
	 */
	public void remoteResponseRcvd(boolean isAccepted) {
		if (!isAccepted) {
			try {
				state.changeState(State.connected);
			} catch (StateChangeException e) {
				e.printStackTrace();
			}
		}
		sharingFiles.remoteResponseNotif(isAccepted);
	}
	
	/**
	 * called to receive a file
	 * @param destDirectory directory where the file will be stored
	 * @throws StateChangeException 
	 */
	public void performReceiveFile(String destDirectory) throws StateChangeException {
		state.changeState(State.busy);
		sharingFiles.localResponseNotif(true, destDirectory);
	}
	
	/**
	 * Used to mark a file as sent
	 * @param isSent
	 */
    public void setFileSent(boolean isSent) {
    	sharingFiles.setIsSent(true);
    }
    
    /**
     * called when a file has been received
     */
	public void fileRcvd(){
		sharingFiles.fileRcvd();
		try {
			state.changeState(State.connected);
		} catch (StateChangeException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * called when the local user refused to receive a file
	 */
	public void refuseReceiveFile() {
		sharingFiles.localResponseNotif(false, null);
	}
	
	/**
	 * called when a bye message has been received
	 * @param username username of the user who has sent the bye message
	 * @param hostName device's hostname of the user who has sent the bye message
	 */
	public void byeRcvd(String username, String hostName){
		try {
			users.checkIn();
			try {
				User u = users.getUser(username, hostName);
				users.removeUser(u); //should be called by controller
			} catch (UserException e) {
				e.printStackTrace();
			}
			users.checkOut();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * called to disconnect the local user
	 * @throws StateChangeException
	 */
	public void performDisconnect() throws StateChangeException{
		state.changeState(State.disconnecting);
	}
	
	/**
	 * called when the bye messages have been broadcasted
	 */
	public void byeSent(){
		try {
			state.changeState(State.disconnected);
		} catch (StateChangeException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * called when the window is closed
	 * @throws StateChangeException
	 */
	public void performCloseWindow() throws StateChangeException {
		state.changeState(State.closing);
	}
	
	
	/**
	 * Checking if input username respects some orthographical rules
	 * @param username
	 * @throws UsernameEmptyException
	 * @throws CharacterNotAcceptedException
	 */
	public String checkUsername(String username) throws UsernameEmptyException,CharacterNotAcceptedException { //protected non??
		if (username.equals("")) {
			throw new UsernameEmptyException();
		}
		if (username.contains("\"") || username.contains("\\") || username.contains("'")) {
			throw new CharacterNotAcceptedException();
		}
		String name = username.replaceAll(" ", "_");
		users.setLocalUsername(name);
		return name;
	}
}
	